using System;
using System.Collections.Generic;
using System.Diagnostics;

namespace Xfinity.Collections
{
    public class BitList
    {
        private List<ulong> list;
        [DebuggerStepThrough]
        public BitList()
        {
            list = new List<ulong>(1);
            list.Add(0);
        }
        [DebuggerStepThrough]
        public BitList(int capacity)
        {
            list = new List<ulong>((int)Math.Ceiling((double)capacity / SizeOfULongInBits));
            list.Add(0);
        }
        public int Add(bool flag)
        {
            count++;
            ulong shift = GetFlag(flag);
            if (count > list.Count * SizeOfULongInBits)
            {
                list.Add(shift);
                return count - 1;
            }
            byte modulated;
            int number;
            GetPosition(count - 1, out modulated, out number);
            SetBit(shift, modulated, number);
            return count - 1;
        }

        private void SetBit(ulong shift, int modulated, int number)
        {
            ulong bucket = list[number];
            bucket &= shift << modulated;
            list[number] = bucket;
        }
        private int count = 0;

        /// <summary>
        /// 
        /// </summary>
        public int Capacity
        {
            [DebuggerStepThrough]
            get { return list.Count * SizeOfULongInBits; }
        }

        /// <summary>
        /// Returns the number of flags
        /// </summary>
        public int Count
        {
            [DebuggerStepThrough]
            get { return count; }
        }

        private const int SizeOfULongInBits = (sizeof(ulong) * 8);
        public bool this[int index]
        {
            [DebuggerStepThrough]
            get
            {
                return Get(index);
            }
            [DebuggerStepThrough]
            set
            {
                Set(index, value);
            }
        }

        public bool Get(int index)
        {
            if (index - 1 > count)
            {
                throw new IndexOutOfRangeException();
            }
            byte modulated;
            int number;
            GetPosition(index, out modulated, out number);
            ulong bucket = list[number];
            ulong flag = 1;
            flag <<= modulated;
            ulong value = flag & bucket;
            return (value != 0);
        }

        public void Set(int index, bool value)
        {
            if (index - 1 > count)
            {
                throw new IndexOutOfRangeException();
            }
            byte modulated;
            int number;
            GetPosition(index, out modulated, out number);
            ulong flag = GetFlag(value);
            SetBit(flag, modulated, number);
        }
        public static BitList operator &(BitList left, BitList right)
        {
            if (left.count != right.count)
            {
                throw new InvalidOperationException();
            }
            BitList value = new BitList();
            for (int i = 0; i < value.list.Count; i++)
            {
                value.list.Add(left.list[i] & right.list[i]);
            }
            value.count = left.count;
            return value;
        }
        public System.Collections.Generic.IEnumerator<bool> GetEnumerator()
        {
            for (int i = 0; i < count; i++)
            {
                yield return Get(i);
            }
        }
        public static BitList operator |(BitList left, BitList right)
        {
            if (left.count != right.count)
            {
                throw new InvalidOperationException();
            }
            BitList value = new BitList();
            for (int i = 0; i < value.list.Count; i++)
            {
                value.list.Add(left.list[i] | right.list[i]);
            }
            value.count = left.count;

            return value;
        }
        public static BitList operator ^(BitList left, BitList right)
        {
            if (left.count != right.count)
            {
                throw new InvalidOperationException();
            }
            BitList value = new BitList();
            for (int i = 0; i < value.list.Count; i++)
            {
                value.list.Add(left.list[i] ^ right.list[i]);
            }
            value.count = left.count;
            return value;
        }

        private const ulong One = (ulong)1;
        private const ulong Zero = (ulong)0;
        private static ulong GetFlag(bool value)
        {
            ulong flag = value ? One : Zero;
            return flag;
        }

        private static void GetPosition(int index, out byte modulated, out int number)
        {
            modulated = (byte)(index % SizeOfULongInBits);
            number = index / SizeOfULongInBits;
        }
    }
}
